/**
 * Urte - unobtrusive rich text editor
 *
 * Copyright (c) 2007 Przemek Sobstel (http://sobstel.org/).
 * Library licensed under MIT license (http://sobstel.org/licenses/mit.txt).
 *
 * Default skin icons licensed under LGPL (http://www.fckeditor.net/license). 
 * They've been based on FCKeditor's Office 2003 skin.
 *
 * $Id: urte.js 2 2007-10-15 09:15:00Z p.sobstel $
 */
(function($) {

	var validBrowser = true;
	
	var htmlActive = false;

    // "abstract" object for all tools
    var abstractTool = {
    	over : function(urte){    		
    	},
    	execute : function(urte){
    		if (this.cmd){
        		urte.execute(this.cmd, null);
				return urte.postProcess();
			}
    	}
    };
    
    // tools definitions
    var tools = {
    	bold : $.extend({},abstractTool,{
    		cmd: 'Bold'
    	}),
    	italic : $.extend({},abstractTool,{
    		cmd : 'Italic'
    	}),
    	underline : $.extend({}, abstractTool,{
    		cmd : 'Underline'
    	}),
    	strike : $.extend({},abstractTool,{
    		cmd : 'StrikeThrough'
    	}),
    	sub : $.extend({},abstractTool,{
    		cmd : 'Subscript'
    	}),
    	sup : $.extend({},abstractTool,{
    		cmd : 'Superscript'
    	}),
    	left : $.extend({},abstractTool,{
    		cmd : 'JustifyLeft'
    	}),
    	center : $.extend({},abstractTool,{
    		cmd : 'JustifyCenter'
    	}),
    	right : $.extend({},abstractTool,{
    		cmd : 'JustifyRight'
    	}),
    	justify : $.extend({},abstractTool,{
    		cmd : 'JustifyFull'
    	}),
    	ol : $.extend({},abstractTool,{
    		cmd : 'InsertOrderedList'
    	}),
    	ul : $.extend({},abstractTool,{
    		cmd : 'InsertUnorderedList'
    	}),
    	outdent : $.extend({},abstractTool,{
    		cmd : 'Outdent'
    	}),
    	indent : $.extend({},abstractTool,{
    		cmd : 'Indent'
    	}),
    	hr : $.extend({},abstractTool,{
    		cmd : 'InsertHorizontalRule'
    	}),
    	link : $.extend({},abstractTool,{
    		execute : function(urte){
    			var node = urte.getNode();
    			if (node.tagName.toLowerCase() != 'a'){ // add
    				var selected = urte.getSelected();
	    			var anchorText = selected ? selected : prompt(texts.linkX.anchortext);
	    			var url = prompt('URL', 'http://');
	    			if (url !== null){
		    			if (anchorText != anchorText.length == 0){
		    				anchorText = url;
		    			}
		    			urte.insert('<a href="'+url+'">'+anchorText+'</a>');
			           	urte.updateElement();
			        }
			    }else{ // edit
			    	_node = $(node);			    	
			    	_node.attr(
			    		'href', 
			    		prompt(
			    			'URL ('+texts.linkX.anchortext+': '+_node.html()+')',
			    			_node.attr('href')
			    		)			    		
			    	);
			    	urte.updateElement();
			    }
		        return urte.handleCarriage();
    		}
    	}),
    	image : $.extend({},abstractTool,{
    		execute : function(urte){
	   			var url = prompt('URL');
	   			if (url !== null){
	    			urte.execute('insertimage', url);
		           	urte.updateElement();
		        }		        		        
		        return urte.handleCarriage();
    		}
    	}),
    	table : $.extend({},abstractTool,{
    		execute : function(urte){    			
    			var rows = prompt('Rows', '2');
			if (!rows) urte.handleCarriage();
    			var cols = prompt('Cols', '2');
			if (!cols) urte.handleCarriage();
    			var header = confirm('Header');
    			// header
    			var output = '<table>';
    			if (header){
    				output += '<tr>';
    				for (var c=0; c<cols; c++){
    					output += '<th> </th>';
    				}
    				output += '</tr>';
    			}    			
    			// rest
    			for (var r=0; r<rows; r++){
    				output += '<tr>';
    				for (var c=0; c<cols; c++){
    					output += '<td> </td>';
    				}
    				output += '</tr>';
    			}    			
    			output += '</table>';
    			urte.insert(output);
    			return urte.postProcess();
    		}
    	}),
    	pre : $.extend({},abstractTool,{
	    	execute : function(urte){
	    		if (urte.getNode().tagName.toLowerCase() != 'pre'){
                	urte.wrap('pre');
                }
	    	}
    	}),
    	hardspace : $.extend({},abstractTool,{
	    	execute : function(urte){
	            if ($.browser.mozilla){
	                var range = urte.getSelection().getRangeAt(0);
	                range.deleteContents();
	                var el = urte.doc.createElement('div');
	                el.innerHTML = '&nbsp';
	                range.insertNode(urte.doc.createTextNode(el.firstChild.nodeValue));
	            }else{
	                urte.insert('&nbsp;');
	            }
				return urte.postProcess();
	    	}
    	}),
    	unformat :  $.extend({},abstractTool,{
    		execute : function(urte){
    			var tag = urte.getNode().tagName.toLowerCase();
    			if (tag != 'body' && tag != 'html'){
    				var _node = $(urte.getNode());
    				var content = _node.html();
    				_node.remove();
    				urte.insert(content);
    				urte.updateElement();				
    			}
    			return urte.handleCarriage();
    		}
    	}),
    	html : $.extend({},abstractTool,{
	    	execute : function(urte){
	    		function closeBox(){
					box.remove();
					urte.toolbar.show();
					urte.iframe.show();
				};
	    		
	            if (!htmlActive){
	            	urte.toolbar.hide();
		        	urte.iframe.hide();
		        	
		        	var textarea = $(document.createElement('textarea')).
		    			width(urte.settings.width-16).
		    			height(urte.settings.height).
		    			val(urte.getHtml());
		        	
					var box = $(document.createElement('div')).
						addClass('urte_box').
						append(textarea).
						append('<br />').
    					append(
							$(document.createElement('a')).attr('href', '#').text(texts.X.update).click(function(){
								urte.doc.body.innerHTML = textarea.val();
								closeBox();
								htmlActive = false;
								return urte.postProcess();
							})
						).
						append(
							$(document.createElement('a')).attr('href', '#').text(texts.X.cancel).click(function(){
								closeBox();
								htmlActive = false;
								return urte.handleCarriage();
							})
						);
					
					if (urte.settings.xhtml){
						textarea.after('<br /><small><b>HTML :</b> ('+texts.X.htmlWarning+')</small><br />');
					}
					
					urte.container.append(box);

	            	htmlActive = true;   
		    		
		    		return urte.postProcess();
		    	}else{
		    		htmlActive = false;
		    		urte.closeBox();
		    		return false;
		    	}
	    	}
    	}),
    	save : $.extend({},abstractTool,{
    		execute : function(urte){
    			eval(urte.settings.onSave+'(urte)');
    		}
    	})
    };

    var standardToolbar = [];
    for (var toolName in tools){
    	standardToolbar.push(toolName);
    }    

    var texts = {};	

	// creating editor
    function Urte(id, settings){
        var urte = this;

        this.element = $('#'+id);
                
        this.id = {
        	container : id+'_container',
            element : id,
            iframe : id+'_iframe',
            tool : id+'_tool',
            toolbar : id+'_toolbar'
        };

		// toolbar, width, height, css, charset, xhtml, htmlEditor, onSave
        this.settings = $.extend({
            toolbar : standardToolbar,
            xhtml : true,           
            stylesheet : '../skins/default/styles_rte.css',
            charset : 'utf-8',
            onSave : function(urte){}
        }, settings);

        if (!this.settings.width){
            this.settings.width = this.element.width();
        }

        if (!this.settings.height){
            this.settings.height = this.element.height();
        }

        this.container = $(document.createElement('div')).
            attr({id:this.id.container}).
            addClass('urte_container');
            
        // inherit some attrs from original element
        $.each(['position', 'left', 'top', 'width', 'margin', 'style'], function(i, n){
        	if (urte.element.css(n)){
				urte.container.css(n, urte.element.css(n));
			}
        });                                    

        this.toolbar = $(document.createElement('div')).
            attr({id:this.id.toolbar}).
            addClass('urte_toolbar');

        this.iframe = $(document.createElement('iframe')).
            attr('id', this.id.iframe).
            addClass('urte_iframe').
            width(this.settings.width).
            height(this.settings.height);

		// hide original element and show editor
        this.element.hide().after(this.container.append(this.toolbar).append(this.iframe));

        for (i=0; i<this.settings.toolbar.length; i++){
            var name = this.settings.toolbar[i];
            if (tools[name]){
              (function(){
                    var localName = name;
                    var tool =  $(document.createElement('div')).
                    	attr('title', texts[localName] ? texts[localName] : localName).
            	        addClass('urte_tool').
            	        addClass('urte_tool_'+localName).
						mousedown(function(e){return tools[localName].execute(urte);}).
	                    appendTo(urte.toolbar);	                   	                    	                   
                })();
            }
        }

        if ($.browser.msie){
            this.win = frames[this.id.iframe];
        }else{
            this.win = this.iframe.get(0).contentWindow;
        }

        this.doc = this.win.document;

        this.doc.open();
        this.doc.write(
            '<html style="border:0">' +
            '<head>' +
            '<meta http-equiv="Content-Type" content="text/html;charset='+this.settings.charset+'" />' +
            (this.settings.stylesheet ? '<link rel="stylesheet" type="text/css" href="'+this.settings.stylesheet+'"" />' : '') +
            '</head>' +
            '<body contenteditable="true">' +
            this.element.html() +
            '</body></html>'
        );
        this.doc.close();

        if (!$.browser.msie){
            this.iframe.get(0).contentDocument.designMode = 'on';
            this.execute('styleWithCSS', false, false);
        }
    };

	// editor methods
    Urte.prototype = {
    	execute : function(cmd, val){
    		return this.doc.execCommand(cmd, false, val);
    	},
        getHtml : function(){
            return this.doc.body.innerHTML;
        },
        getSelection : function(){
            if (this.win.getSelection){
                return this.win.getSelection();
            }else if (this.doc.getSelection){
                return this.doc.getSelection();
            }else if (this.doc.selection){
                return this.doc.selection.createRange();
            }
        },
        getSelected : function(){
            var selection = this.getSelection();
            if (!$.browser.msie){
                return selection.toString();
            }else{
                return selection.htmlText;
            }
        },
        isSelected : function(){
        	return (this.getSelected().length > 0);
        },
        getNode : function(){
        	var node;
        	if (!$.browser.msie){    		
                node = this.getSelection().getRangeAt(0).startContainer.parentNode;
            }else{
                node = this.doc.selection.createRange().parentElement();
            }
	        return node;
        },
        insert : function(html){
            if ($.browser.msie){
                var sel = this.getSelection();
                // sel.collapse(true)
                sel.pasteHTML(html);
            }else{
                this.execute('inserthtml', html);
            }
        },
        wrap : function(tag){
        	var selected = this.getSelected();
            if (selected.length > 0){
                this.insert('<'+tag+'>'+selected+'</'+tag+'>');
            }
        },
        updateElement : function(){
        	var html = this.getHtml();
        	if (this.settings.xhtml){
        		html = html.replace(/<br>/g, "<br />");
				html = html.replace(/<hr>/g, "<hr />");
				html = html.replace(/(<img [^>]+[^\/])>/g, "$1 />");
        	}
        	this.element.html();
        },
        handleCarriage : function(){
        	if ($.browser.msie){
				// error is invoked, otherwise carriage does not return to edit area after
				// clicking tool. yeah, i know it's really weird, but it works in IE,
				// unlike preventDefault, returnValue and stopPropagation
               	fakeError();
            }
	        return false;
        },
        postProcess : function(){
        	this.updateElement();
        	return this.handleCarriage();
        },
	  restore : function(){
		this.container.remove();
		this.element.show();
	  }
	};
			    
    $.fn.Urte = function(settings){
	    return this.each(function(){
            if (validBrowser){
                new Urte(this.id, settings);
            }
        });
    };

    $.Urte = {
        addTool : function(name, tool){
            tools[name] = $.extend({}, abstractTool, tool);
        },
        addTexts : function(newTexts){
        	$.extend(texts, newTexts);
        }
    };

})(jQuery);